var struct = {
    databse: 'name',
    version: 1,
    sort: "next", //prev
    columns: {
        keyPath: 'id',
        index: [{
            email: {
                unique: true
            }
        }]
    }
};

function fw4wdb(database, version, columns) {
    return new(function (database, version, columns) {
        struct.database = database;
        struct.version = version;
        struct.columns = columns;
        var op = {
            'addAll': function (values) {
                db_add(struct, values);
            },
            'add': function (values) {
                db_add(struct, values);
            },
            'putAll': function (values) {
                db_put(struct, values);
            },
            'put': function (values) {
                db_put(struct, values);
            },
            'clear': function (values) {
                db_clear(struct);
            },
            'del': function (key) {
                db_del(struct, key);
            },
            'each': function (callback) {
                db_each(struct, callback);
            },
            'index': function (key) {
                return db_index(struct, key);
            }
        };
        this.open = function (callback) {
            struct.op = op;
            db_open(struct, function (struct) {
                callback(struct.op);
            });
        };
    })(database, version, columns);
}


if (typeof $ === 'function') {
    $.wdb = fw4wdb;
}

function db_deleteDB(database) {
    if (!window.indexedDB) {
        window.indexedDB = window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    }
    window.indexedDB.deleteDataBase(database);
}

function db_open(struct, callback) {
    if (!window.indexedDB) {
        window.indexedDB = window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    }
    if (struct.version == undefined || typeof struct.version != 'number') {
        console.error("version undefiend", typeof struct.version);
    } else {
        var request = window.indexedDB.open(struct.database, struct.version);
        request.onsuccess = function (event) {
            console.log("open success");
            struct.db = request.result;
            callback(struct);
            struct.db.close();
        };
        request.onerror = function (event) {
            console.log("open error", event.target.errorCode);
        };
        request.onblocked = function (event) {
            alert("Please close all other tabs with this site open!");
        };
        request.onupgradeneeded = function (event) {
            keyPath = {
                autoIncrement: true
            };
            if (struct.columns != undefined && struct.columns.keyPath != undefined) {
                console.log("struct columns ", struct.columns.keyPath);
                keyPath = {
                    'keyPath': struct.columns.keyPath
                };
            }
            var db = event.target.result;

            db.onversionchange = function (event) {
                db.close();
                console.log("A new version of this page is ready. Please reload!");
                window.location.href = window.location.href;
            };
            if (!db.objectStoreNames.contains(struct.database)) {
                console.log("create table");
                var store = db.createObjectStore(struct.database, keyPath);
                if (struct.columns != undefined && struct.columns.index != undefined) {
                    var index = struct.columns.index;
                    for (var i = 0; i < index.length; i++) {
                        var fields = index[i];
                        for (var field in fields) {
                            console.log(field, fields[field]);
                            console.log("aaa", fields[field]);
                            store.createIndex(field, field, fields[field]);
                        }
                    }
                }
            }
        };
    }

}

function getTransaction(struct, type) {
    return struct.db.transaction(struct.database, type).objectStore(struct.database);
}

function db_add(struct, values) {
    var objectStore = getTransaction(struct, 'readwrite');
    objectStore.oncomplete = function (event) {}
    objectStore.onerror = function (event) {}
    if (!Array.isArray(values)) {
        values = [values];
    }
    var len = values.length;
    for (var i = 0; i < len; i++) {
        objectStore.add(values[i]);
    }
}

function db_put(struct, values) {
    var objectStore = getTransaction(struct, 'readwrite');
    objectStore.oncomplete = function (event) {}
    objectStore.onerror = function (event) {}
    if (!Array.isArray(values)) {
        values = [values];
    }
    var len = values.length;
    for (var i = 0; i < len; i++) {
        objectStore.put(values[i]);
    }
}

function db_each(struct, callback) {
    range(struct, struct.columns.keyPath, null, callback);
}

function db_del(struct, key) {
    var objectStore = getTransaction(struct, "readwrite");
    objectStore.delete(key);
    objectStore.onsuccess = function (event) {
        console.log("delete success");
    }
}

function db_clear(struct) {
    var objectStore = getTransaction(struct, "readwrite");
    objectStore.clear();
    objectStore.onsuccess = function (event) {
        console.log("clear success");
    }
}

function range(struct, key, range, callback) {
    console.log("range:", key);
    IDBKeyRange.NEXT = struct.sort == undefined ? "next" : struct.sort; // next | prev
    var objectStore = getTransaction(struct, 'readonly');
    var index = objectStore.index(key);
    index.openCursor(range, IDBKeyRange.NEXT).onsuccess = function (event) {
        var cursor = event.target.result;
        if (callback == undefined || typeof callback != 'function') return;
        if (!cursor) return;
        callback(cursor.value);
        cursor.continue();
    };
}

function db_index(struct, key) {
    return {
        'eq': function (x) {
            return {
                'range': function (callback) {
                    var keyRange = IDBKeyRange.only(x);
                    range(struct, key, keyRange, callback);
                }
            };
        },
        'gt': function (x) {
            return {
                'lt': function (y) {
                    return {
                        'range': function (callback) {
                            var keyRange = IDBKeyRange.bound(x, y, true, true);
                            range(struct, key, keyRange, callback);
                        }
                    };
                },
                'le': function (y) {
                    return db_index(struct, key).le(y).gt(x);
                },
                'range': function (callback) {
                    var keyRange = IDBKeyRange.lowerBound(x, true);
                    range(struct, key, keyRange, callback);
                }
            };
        },
        //lt
        'lt': function (x) {
            return {
                'gt': function (y) {
                    return db_index(struct, key).gt(y).lt(x);
                },
                'ge': function (y) {
                    return db_index(struct, key).ge(y).lt(x);
                },
                'range': function (callback) {
                    var keyRange = IDBKeyRange.upperBound(x, true);
                    range(struct, key, keyRange, callback);
                }
            };
        },
        //ge
        'ge': function (x) {
            return {
                'lt': function (y) {
                    return {
                        'range': function (callback) {
                            var keyRange = IDBKeyRange.bound(x, y, false, true);
                            range(struct, key, keyRange, callback);
                        }
                    };
                },
                'le': function (y) {
                    return {
                        'range': function (callback) {
                            var keyRange = IDBKeyRange.bound(x, y);
                            range(struct, key, keyRange, callback);
                        }
                    };
                },
                'range': function (callback) {
                    var keyRange = IDBKeyRange.lowerBound(x);
                    range(struct, key, keyRange, callback);
                }
            };
        },
        //le
        'le': function (x) {
            return {
                'gt': function (y) {
                    return {
                        'range': function (callback) {
                            var keyRange = IDBKeyRange.boud(y, x, true, false);
                            range(struct, key, keyRange, callback);
                        }
                    };
                },
                'ge': function (y) {
                    return db_index(struct, key).ge(y).le(x);
                },
                'range': function (callback) {
                    var keyRange = IDBKeyRange.upperBound(x);
                    range(struct, key, keyRange, callback);
                }
            };
        }
    };
}
